package jk.framework.spring.security;

import jk.framework.spring.security.authentication.AuthenticationDetailsProvider;
import jk.framework.spring.security.authorization.SecurityResourcesService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * @author
 */
public class SecuritySupport implements BeanFactoryAware, InitializingBean {


    private ConfigurableListableBeanFactory beanFactory;

    private ObjectPostProcessor objectPostProcessor;

    @Override
    public void afterPropertiesSet() throws Exception {
        /**
         * 获得@EnableSecurity 注入AutowireBeanFactoryObjectPostProcessor 类
         * 如果没有/多个 则报错
         */
        try {
            objectPostProcessor = beanFactory.getBean(ObjectPostProcessor.class);
        } catch (Exception e) {
            throw e;
        }

    }

    /**
     * AuthenticationToken携带UserDetails信息
     * @param managerBuilder
     * @return
     */
    public AbstractUserDetailsAuthenticationProvider authenticationDetailsProvider(AuthenticationManagerBuilder managerBuilder)throws Exception {
        UserDetailsService detailsService = beanFactory.getBean(UserDetailsService.class);
        PasswordEncoder passwordEncoder = beanFactory.getBean(PasswordEncoder.class);
        return authenticationDetailsProvider(managerBuilder, detailsService, passwordEncoder);
    }

    /**
     * AuthenticationToken携带UserDetails信息
     * @param managerBuilder
     * @param service
     * @param passwordEncoder
     * @return
     */
    public AbstractUserDetailsAuthenticationProvider authenticationDetailsProvider(AuthenticationManagerBuilder managerBuilder, UserDetailsService service, PasswordEncoder passwordEncoder) throws Exception {
        AuthenticationDetailsProvider provider = new AuthenticationDetailsProvider(service);
        provider.setPasswordEncoder(passwordEncoder);
        //模拟bean初始化流程
        objectPostProcessor.postProcess(provider);
        managerBuilder.authenticationProvider(provider);
        return provider;
    }

    /**
     * 动态权限配置
     * @param security
     * @return
     * @throws Exception
     */
    public <T extends SecurityResourcesService> DynamicAuthorizationConfig dynamicAuthorize(HttpSecurity security) throws Exception {

        DynamicAuthorizationConfig config = new DynamicAuthorizationConfig();

        security.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O processor) {
                config.configure();
                processor.setAccessDecisionManager(config.getDecisionManager());
                processor.setSecurityMetadataSource(config.getSecurityMetadataSource());
                return processor;
            }
        }).anyRequest().authenticated();

        return config;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
            throw new IllegalArgumentException(
                    this.getClass().getSimpleName() + " requires a ConfigurableListableBeanFactory: " + beanFactory);
        }
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }


}
